#include<iostream>
#include<queue>
#include<pthread.h>
using namespace std;
#define MAX_QUEUE 5

template <class T>
class BlockQueue{
public:
    BlockQueue(int capacity=MAX_QUEUE)
    :_capacity(capacity)
    {pthread_mutex_init(&_mutex,NULL);
     pthread_cond_init(&_cond_pro,NULL);
     pthread_cond_init(&_cond_cus,NULL);}
    bool Push(const T& data){//生产线程
        //队列满了就要阻塞，不能继续入队
        pthread_mutex_lock(&_mutex);
        while(_queue.size()==_capacity){
            pthread_cond_wait(&_cond_pro,&_mutex);
        }
        _queue.push(data);
        pthread_mutex_unlock(&_mutex);
        pthread_cond_signal(&_cond_cus);
        return true;
    }
    bool Pop(T* data){//消费线程
        //队列为空则阻塞
        pthread_mutex_lock(&_mutex);
        while(_queue.empty()==true){
            pthread_cond_wait(&_cond_cus,&_mutex);
        }
        *data=_queue.front();
        _queue.pop();
        pthread_mutex_unlock(&_mutex);
        pthread_cond_signal(&_cond_pro);
        return true;
    }
    ~BlockQueue(){
    pthread_mutex_destroy(&_mutex);
    pthread_cond_destroy(&_cond_pro);
    pthread_cond_destroy(&_cond_cus);
    }
private:
    queue<T> _queue;
    int _capacity;
    pthread_mutex_t _mutex;
    pthread_cond_t _cond_pro;
    pthread_cond_t _cond_cus; 
};
#define MAX_THREAD 4
void *producer(void *arg){
    BlockQueue<int> *bq=(BlockQueue<int>*)arg;
    int data=0;
    while(1){
        bq->Push(data);
        //cout内部有缓冲区线程不安全 printf内部有互斥锁是线程安全的
        cout<<pthread_self()<<"push data:"<<data++<<endl;
    }
    return NULL;
}
void *consumer(void *arg){
    BlockQueue<int> *bq=(BlockQueue<int>*)arg;
    int data=0;
    while(1){
        bq->Pop(&data);
        cout<<pthread_self()<<"push data:"<<data<<endl;
    }
    return NULL;
}
int main(){
    BlockQueue<int> bq;
    pthread_t ptid[MAX_THREAD],ctid[MAX_THREAD];
    int ret;
    for(int i=0;i<MAX_THREAD;i++){
        ret=pthread_create(&ptid[i],NULL,producer,&bq);
        if(ret!=0){
            cout<<"create thread fail!\n"<<endl;
            return -1;
        }
        ret=pthread_create(&ctid[i],NULL,consumer,&bq);
        if(ret!=0){
            cout<<"create thread fail!\n"<<endl;
            return -1;
        }
    }
    for(int i=0;i<MAX_THREAD;i++){
        pthread_join(ptid[i],NULL);
        pthread_join(ctid[i],NULL);
    }
    return 0;
}